package command

import (
	"gitee.com/csingo/cContext"
	"gitee.com/csingo/cLog"
	"satis-go/app/collector/model"
	"satis-go/app/collector/service"
	"satis-go/base/collector"
	"satis-go/config/vars"
	"sync"
	"time"
)

type PackagesDownloadCommand struct{}

func (i *PackagesDownloadCommand) CommandName() (app, name string) {
	return "collector", "PackagesDownloadCommand"
}

type PackagesQueue struct {
	QueueLock                *sync.Mutex
	WaitPackages             []*model.Packages
	DownloadPackages         map[string]*model.Packages
	GitDownloadChannel       chan *model.Packages
	PackagistDownloadChannel chan *model.Packages
}

func (i *PackagesQueue) Push(pkg *model.Packages) {
	i.QueueLock.Lock()
	defer i.QueueLock.Unlock()

	if _, ok := i.DownloadPackages[pkg.Name]; ok {
		return
	}

	i.WaitPackages = append(i.WaitPackages, pkg)
	i.DownloadPackages[pkg.Name] = pkg
}

func (i *PackagesQueue) Pop() (pkg *model.Packages) {
	i.QueueLock.Lock()
	defer i.QueueLock.Unlock()

	if len(i.WaitPackages) == 0 {
		return nil
	}

	pkg = i.WaitPackages[0]
	delete(i.DownloadPackages, pkg.Name)

	return
}

var pkgQueue = &PackagesQueue{
	QueueLock:                &sync.Mutex{},
	WaitPackages:             []*model.Packages{},
	DownloadPackages:         make(map[string]*model.Packages),
	GitDownloadChannel:       make(chan *model.Packages),
	PackagistDownloadChannel: make(chan *model.Packages),
}

func (i *PackagesDownloadCommand) Download() {
	// read packagist
	go func() {
		m := &model.Packages{}
		for {
			time.Sleep(time.Duration(vars.CollectorConfig.WaitSecond) * time.Second)
			ctx := cContext.New()
			pkgs, err := m.FindByType(ctx, 0)
			if err != nil {
				cLog.WithContext(ctx, map[string]interface{}{
					"pkgs": pkgs,
					"err":  err.Error(),
				}).Error("PackagesDownloadCommand.downloadComposer:读取packagist数据异常")
				continue
			}
			if len(pkgs) <= 0 {
				continue
			}

			for _, pkg := range pkgs {
				pkgQueue.PackagistDownloadChannel <- pkg
			}
		}
	}()

	// read git
	go func() {
		m := &model.Packages{}
		for {
			time.Sleep(time.Duration(vars.CollectorConfig.WaitSecond) * time.Second)
			ctx := cContext.New()
			pkgs, err := m.FindByType(ctx, 1)
			if err != nil {
				cLog.WithContext(ctx, map[string]interface{}{
					"pkgs": pkgs,
					"err":  err.Error(),
				}).Error("PackagesDownloadCommand.downloadComposer:读取packagist数据异常")
				continue
			}
			if len(pkgs) <= 0 {
				continue
			}

			for _, pkg := range pkgs {
				pkgQueue.GitDownloadChannel <- pkg
			}
		}
	}()

	// packagist 下载
	total := vars.CollectorConfig.Concurrent
	for index := 0; index < total; index++ {
		go func() {
			for {
				select {
				case pkg := <-pkgQueue.PackagistDownloadChannel:
					i.handlePackagist(pkg)
				}
			}
		}()
	}

	// git 下载
	go func() {
		for {
			select {
			case pkg := <-pkgQueue.GitDownloadChannel:
				i.handleGit(pkg)
			}
		}
	}()
}

func (i *PackagesDownloadCommand) handlePackagist(pkg *model.Packages) {
	ctx := cContext.New()
	defer func() {
		if r := recover(); r != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"pkg": pkg,
			}).Error("PackagesDownloadCommand.downloadComposer:panic异常")
		}
	}()

	pkgQueue.DownloadPackages[pkg.Name] = pkg
	defer delete(pkgQueue.DownloadPackages, pkg.Name)

	svc := &service.PackagistService{}

	if !vars.CollectorConfig.Enable {
		cLog.WithContext(ctx, nil).Debug("PackagesDownloadCommand.Read:收集器不可用")
		return
	}
	req := &collector.PackagistServiceDownloadReq{
		Name: pkg.Name,
		Url:  pkg.Url,
	}
	rsp, err := svc.Download(ctx, req)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req": req,
			"pkg": pkg,
			"err": err.Error(),
		}).Error("PackagesDownloadCommand.Download:包下载异常")
		return
	}
	if rsp == nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req": req,
			"rsp": rsp,
			"pkg": pkg,
		}).Error("PackagesDownloadCommand.Download:包未更新")
		return
	}
	if len(rsp.Versions) > 0 && rsp.IsUpdateVersion {
		err := (&model.Versions{}).Save(ctx, pkg.ID, rsp.Versions)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"pkg": pkg,
				"rsp": rsp,
			}).Error("PackagesDownloadCommand.Download:保存版本失败")
		}
	}
}
func (i *PackagesDownloadCommand) handleGit(pkg *model.Packages) {
	ctx := cContext.New()
	defer func() {
		if r := recover(); r != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"pkg": pkg,
			}).Error("PackagesDownloadCommand.downloadComposer:panic异常")
		}
	}()

	pkgQueue.DownloadPackages[pkg.Name] = pkg
	defer delete(pkgQueue.DownloadPackages, pkg.Name)

	svc := &service.GitService{}

	if !vars.CollectorConfig.Enable {
		cLog.WithContext(ctx, nil).Debug("PackagesDownloadCommand.Read:收集器不可用")
		return
	}
	req := &collector.GitServiceDownloadReq{
		Name: pkg.Name,
		Url:  pkg.Url,
	}
	rsp, err := svc.Download(ctx, req)
	if err != nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req": req,
			"pkg": pkg,
			"err": err.Error(),
		}).Error("PackagesDownloadCommand.Download:包下载异常")
		return
	}
	if rsp == nil {
		cLog.WithContext(ctx, map[string]interface{}{
			"req": req,
			"rsp": rsp,
			"pkg": pkg,
		}).Error("PackagesDownloadCommand.Download:包未更新")
		return
	}
	if len(rsp.Versions) > 0 && rsp.IsUpdateVersion {
		err := (&model.Versions{}).Save(ctx, pkg.ID, rsp.Versions)
		if err != nil {
			cLog.WithContext(ctx, map[string]interface{}{
				"pkg": pkg,
				"rsp": rsp,
			}).Error("PackagesDownloadCommand.Download:保存版本失败")
		}
	}
}
